﻿using System;
using System.Security.Cryptography;
using System.Text;
using System.Diagnostics;
using System.Collections.Generic;
using System.Web.Security;
using Microsoft.Security.Application;
using System.Text.RegularExpressions;
using Encoder = Microsoft.Security.Application.Encoder;

namespace SilenthinkCommon.Extension
{
    public static class StringExtension
    {
        private static readonly Regex WebUrlExpression = new Regex(@"(http|https)://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?", RegexOptions.Singleline | RegexOptions.Compiled);
        private static readonly Regex EmailExpression = new Regex(@"^([0-9a-zA-Z]+[-._+&])*[0-9a-zA-Z]+@([-0-9a-zA-Z]+[.])+[a-zA-Z]{2,6}$", RegexOptions.Singleline | RegexOptions.Compiled);
        private static readonly Regex StripHtmlExpression = new Regex("<\\S[^><]*>", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled);

        private static readonly char[] IllegalUrlCharacters = new[] { ';', '/', '\\', '?', ':', '@', '&', '=', '+', '$', ',', '<', '>', '#', '%', '.', '!', '*', '\'', '"', '(', ')', '[', ']', '{', '}', '|', '^', '`', '~', '–', '‘', '’', '“', '”', '»', '«' };

        [DebuggerStepThrough]
        public static bool IsNullOrEmpty(this string target)
        {
            return string.IsNullOrEmpty(target);
        }

        [DebuggerStepThrough]
        public static bool IsMatch(this string target, string pattern)
        {
            return target != null && Regex.IsMatch(target, pattern);
        }

        [DebuggerStepThrough]
        public static string Match(this string target, string pattern)
        {
            return target == null ? string.Empty : Regex.Match(target, pattern).Value;
        }
        /// <summary>
        /// 是否为密码字符串
        /// 要求密码长度为6-18位，包含至少1个特殊字符，2个数字，2个大写字母和一些小写字母。 
        /// </summary>
        /// <param name="target"></param>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static bool IsPassword(this string target)
        {
            const string reg = @"^(?=^.{6,18}$)(?=(?:.*?\d){2})(?=.*[a-z])(?=(?:.*?[A-Z]){2})(?=(?:.*?[!@#$%*()_+^&}{:;?.]){1})(?!.*\s)[0-9a-zA-Z!@#$%*()_+^&]*$";
            return !string.IsNullOrEmpty(target) && IsMatch(target, reg);
        }
        public static bool IsMobile(this string target)
        {
            const string reg = @"^(13\d{9}$)|(15[0135-9]\d{8}$)|(18[2679]\d{8})$";
            return !string.IsNullOrEmpty(target) && IsMatch(target, reg);
        }
        [DebuggerStepThrough]
        public static bool IsWebUrl(this string target)
        {
            return !string.IsNullOrEmpty(target) && WebUrlExpression.IsMatch(target);
        }

        [DebuggerStepThrough]
        public static bool IsEmail(this string target)
        {
            return !string.IsNullOrEmpty(target) && EmailExpression.IsMatch(target);
        }

        [DebuggerStepThrough]
        public static string NullSafe(this string target)
        {
            return (target ?? string.Empty).Trim();
        }

        [DebuggerStepThrough]
        public static string FormatWith(this string target, params object[] args)
        {
            return string.Format(target, args);
        }

        [DebuggerStepThrough]
        public static string Reverse(this string target)
        {
            var input = target.ToCharArray();
            var output = new char[target.Length];
            for (var i = 0; i < input.Length; i++)
                output[input.Length - 1 - i] = input[i];
            return new string(output);
        }
        //[DebuggerStepThrough]
        //public static string Hash(this string target)
        //{
        //    Check.Argument.IsNotEmpty(target, "target");

        //    using (MD5 md5 = MD5.Create())
        //    {
        //        byte[] data = Encoding.Unicode.GetBytes(target);
        //        byte[] hash = md5.ComputeHash(data);

        //        return Convert.ToBase64String(hash);
        //    }
        //}

        [DebuggerStepThrough]
        public static string WrapAt(this string target, int index)
        {
            const int dotCount = 3;
            return (target.Length <= index) ? target : string.Concat(target.Substring(0, index - dotCount), new string('.', dotCount));
        }

        [DebuggerStepThrough]
        public static string StripHtml(this string target)
        {
            return StripHtmlExpression.Replace(target, string.Empty);
        }

        [DebuggerStepThrough]
        public static string ToPassword(this string target)
        {
            return FormsAuthentication.HashPasswordForStoringInConfigFile(target, "MD5");
        }

        [DebuggerStepThrough]
        public static string ToCamel(this string target)
        {
            if (target.IsNullOrEmpty()) return target;
            return target[0].ToString().ToLower() + target.Substring(1);
        }

        [DebuggerStepThrough]
        public static string ToPascal(this string target)
        {
            if (target.IsNullOrEmpty()) return target;
            return target[0].ToString().ToUpper() + target.Substring(1);
        }
        //[DebuggerStepThrough]
        //public static Guid ToGuid(this string target)
        //{
        //    var result = Guid.Empty;

        //    if ((!string.IsNullOrEmpty(target)) && (target.Trim().Length == 22))
        //    {
        //        var encoded = string.Concat(target.Trim().Replace("-", "+").Replace("_", "/"), "==");

        //        try
        //        {
        //            var base64 = Convert.FromBase64String(encoded);

        //            result = new Guid(base64);
        //        }
        //        catch (FormatException)
        //        {
        //        }
        //    }

        //    return result;
        //}
        /// <summary>
        /// 转全角(SBC case)
        /// </summary>
        /// <param name="target">任意字符串</param>
        /// <returns>全角字符串</returns>
        public static string ToSbc(this string target)
        {
            var c = target.ToCharArray();
            for (var i = 0; i < c.Length; i++)
            {
                if (c[i] == 32)
                {
                    c[i] = (char)12288;
                    continue;
                }
                if (c[i] < 127)
                    c[i] = (char)(c[i] + 65248);
            }
            return new string(c);
        }

        /// <summary>
        /// 转半角(DBC case)
        /// </summary>
        /// <param name="target">任意字符串</param>
        /// <returns>半角字符串</returns>
        public static string ToDbc(this string target)
        {
            var c = target.ToCharArray();
            for (var i = 0; i < c.Length; i++)
            {
                if (c[i] == 12288)
                {
                    c[i] = (char)32;
                    continue;
                }
                if (c[i] > 65280 && c[i] < 65375)
                    c[i] = (char)(c[i] - 65248);
            }
            return new string(c);
        }

        [DebuggerStepThrough]
        public static T ToEnum<T>(this string target, T defaultValue) where T : IComparable, IFormattable
        {
            var convertedValue = defaultValue;

            if (!string.IsNullOrEmpty(target))
            {
                try
                {
                    convertedValue = (T)Enum.Parse(typeof(T), target.Trim(), true);
                }
                catch (ArgumentException)
                {
                }
            }

            return convertedValue;
        }

        #region Security
        [DebuggerStepThrough]
        public static string UrlEncode(this string target)
        {
            return Encoder.UrlEncode(target);
        }
        [DebuggerStepThrough]
        public static string HtmlEncode(this string target)
        {
            return Encoder.HtmlEncode(target);
        }
        [DebuggerStepThrough]
        public static string JavaScriptEncode(this string target)
        {
            return Encoder.JavaScriptEncode(target);
        }
        [DebuggerStepThrough]
        public static string JavaScriptEncode(this string target, bool emitQuote)
        {
            return Encoder.JavaScriptEncode(target, emitQuote);
        }
        [DebuggerStepThrough]
        public static string CssEncode(this string target)
        {
            return Encoder.CssEncode(target);
        }
        [DebuggerStepThrough]
        public static string XmlEncode(this string target)
        {
            return Encoder.XmlEncode(target);
        }

        [DebuggerStepThrough]
        public static string HtmlAttributeEncode(this string target)
        {
            return Encoder.HtmlAttributeEncode(target);
        }
        [DebuggerStepThrough]
        public static string XmlAttributeEncode(this string target)
        {
            return Encoder.XmlAttributeEncode(target);
        }
        [DebuggerStepThrough]
        public static string GetSafeHtml(this string target)
        {
            return Sanitizer.GetSafeHtml(target);
        }
        [DebuggerStepThrough]
        public static string GetSafeHtmlFragment(this string target)
        {
            return Sanitizer.GetSafeHtmlFragment(target);
        }

        private static readonly Regex TagRegex = new Regex("<[^<>]*>", RegexOptions.Compiled | RegexOptions.Singleline);
        [DebuggerStepThrough]
        public static string CleanHtmlTag(this string target)
        {
            return target.CleanHtmlTag(null);
        }
        [DebuggerStepThrough]
        public static string CleanHtmlTag(this string targets, string exceptionPattern)
        {
            if (!string.IsNullOrEmpty(exceptionPattern))
                return
                    new Regex(string.Format("<(?!{0})[^<>]*>", exceptionPattern),
                              RegexOptions.Compiled | RegexOptions.Singleline).Replace(targets, string.Empty);

            return TagRegex.Replace(targets, string.Empty);
        }
        #endregion
        [DebuggerStepThrough]
        public static string Replace(this string target, ICollection<string> oldValues, string newValue)
        {
            oldValues.ForEach(oldValue => target = target.Replace(oldValue, newValue));
            return target;
        }

        ///   <summary>   
        ///   截取字符串   
        ///   </summary>   
        ///   <param   name="strInput">传入字符串</param>   
        ///   <param   name="strEnd">发生截取后的后缀（例如：...）</param>   
        ///   <param   name="intLen">截取后的长度（包括后缀，全角占两位）</param>   
        ///   <returns>截取好的字符串</returns>   
        public static string ToCutString(this string strInput, string strEnd, int intLen)
        {
            strInput = strInput.Trim();
            var byteLen = Encoding.Default.GetByteCount(strInput);
            if (byteLen <= intLen)
            {
                return strInput;
            }
            var resultStr = string.Empty;
            for (var i = 0; i < strInput.Length; i++)
            {
                if (Encoding.Default.GetByteCount(resultStr) < intLen - strEnd.Length)
                {
                    resultStr += strInput.Substring(i, 1);
                }
                else
                {
                    break;
                }
            }
            return resultStr + strEnd;
        }

        /// <summary>
        /// 如果第一个字符为英文直接返回英文首字母并转换为大写
        /// 如果第一个字符为中文则返回第一个字符的中文拼音的首字母并转换为大写
        /// </summary>
        /// <param name="target">字符</param>
        /// <returns>大写拼音首字母</returns>
        public static string ToFirstChsSpell(this string target)
        {
            var cnChar = target.Substring(0, 1);
            var arrCn = Encoding.Default.GetBytes(cnChar);
            if (arrCn.Length > 1)
            {
                int area = arrCn[0];

                int pos = arrCn[1];
                var code = (area << 8) + pos;
                int[] areacode = { 45217, 45253, 45761, 46318, 46826, 47010, 47297, 47614, 48119, 48119, 49062, 49324, 49896, 50371, 50614, 50622, 50906, 51387, 51446, 52218, 52698, 52698, 52698, 52980, 53689, 54481 };
                for (var i = 0; i < 26; i++)
                {
                    var max = 55290;
                    if (i != 25) max = areacode[i + 1];
                    if (areacode[i] <= code && code < max)
                    {
                        return Encoding.Default.GetString(new[] { (byte)(65 + i) });
                    }
                }
                return string.Empty;
            }
            return cnChar;
        }

        /// <summary>
        /// Aes加密 返回HexString
        /// </summary>
        /// <param name="target"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string AesEncrypt(this string target, string key)
        {
            var keyArray = Encoding.UTF8.GetBytes(key);
            var toEncryptArray = Encoding.UTF8.GetBytes(target);
            var rDel = new RijndaelManaged {Key = keyArray, Mode = CipherMode.ECB, Padding = PaddingMode.PKCS7};
            var cTransform = rDel.CreateEncryptor();
            var bytes = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
            return bytes.ToHex();
        }

        /// <summary>
        /// Aes解密
        /// </summary>
        /// <param name="target">HexString</param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string AesDecrypt(this string target, string key)//"2rV92fe_e3.tu5A8"
        {
            var keyArray = Encoding.UTF8.GetBytes(key);
            var toEncryptArray = HexToByte(target);
            var rijndaelManaged = new RijndaelManaged {Key = keyArray,Mode = CipherMode.ECB,Padding = PaddingMode.PKCS7};
            var cryptoTransform = rijndaelManaged.CreateDecryptor();
            var resultArray = cryptoTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
            return Encoding.UTF8.GetString(resultArray);
        }
        /// <summary>
        /// string转16进制byte[]
        /// </summary>
        /// <param name="hexString"></param>
        /// <returns></returns>
        public static byte[] HexToByte(this string hexString)
        {
            var returnBytes = new byte[hexString.Length / 2];
            for (var i = 0; i < returnBytes.Length; i++)
                returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
            return returnBytes;
        }
        /// <summary>
        /// 16进制byte[]转string
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static string ToHex(this IEnumerable<byte> bytes) 
        {
            var hex = new StringBuilder();
            foreach (var b in bytes)
            {
                hex.AppendFormat("{0:X2}", b);
            }
            return hex.ToString(); 
        }

    }
}